package main

import (
	"encoding/xml"
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strconv"
	"strings"
)

// 解析XML的结构体
type XMLRoot struct {
	XMLName xml.Name  `xml:"root"`
	Datas   []XMLData `xml:"data"`
}

type XMLData struct {
	Attrs []xml.Attr `xml:",any,attr"`
}

// 命令行参数结构
type Options struct {
	InFile    string
	OutPath   string
	Key       string //key
	Simple    bool
	Int64s    map[string]bool
	Uint64s   map[string]bool
	Strings   map[string]bool
	Float64s  map[string]bool
	Float32s  map[string]bool
	ArrPrefix map[string]string
	SplitArrs map[string]string
	Discard   map[string]bool
	Options   map[string]bool
}

type TemplateData struct {
	shortName       string
	this            string
	Key             string
	StructName      string
	fileName        string
	Fields          []FieldInfo
	ResourceArrays  []ResourceArray
	AttributeArrays []AttributeArray
	SliceArrays     []SliceArray
	SplitArrays     []SplitData
	noteMap         map[string]string
}

type FieldInfo struct {
	Name    string
	Type    string
	XMLTag  string
	Comment string
}

type ResourceArray struct {
	Name      string
	TypePairs [][3]string
}

type AttributeArray struct {
	Name      string
	TypePairs [][2]string
	calcFunc  string
	addFunc   string
}

type SliceArray struct {
	Name      string
	TypePairs []string
}

type SplitData struct {
	fieldName  string
	structName string
}

// 解析命令行参数
func parseFlags() *Options {
	opt := &Options{
		Int64s:    make(map[string]bool),
		Uint64s:   make(map[string]bool),
		Strings:   make(map[string]bool),
		Float64s:  make(map[string]bool),
		Float32s:  make(map[string]bool),
		ArrPrefix: make(map[string]string),
		SplitArrs: make(map[string]string),
		Discard:   make(map[string]bool),
		Options:   make(map[string]bool),
	}
	flag.StringVar(&opt.InFile, "in", "", "输入xml文件")
	flag.StringVar(&opt.OutPath, "out", ".", "输出go文件目录")
	flag.StringVar(&opt.Key, "id", "", "主键")
	flag.BoolVar(&opt.Simple, "simple", true, "简单模式")
	var i64, u64, str, arr, discard, f64, f32, option, split_arrs, split_flag string
	flag.StringVar(&i64, "i64", "", "int64类型字段,逗号分隔")
	flag.StringVar(&u64, "u64", "", "uint64类型字段,逗号分隔")
	flag.StringVar(&str, "str", "", "string类型字段,逗号分隔")
	flag.StringVar(&arr, "arr_prefix", "", "切片前缀,如skill,passive")
	flag.StringVar(&discard, "discard", "", "忽略字段,逗号分隔")
	flag.StringVar(&f64, "f64", "", "float64类型字段,逗号分隔")
	flag.StringVar(&f32, "f32", "", "float32类型字段,逗号分隔")
	flag.StringVar(&option, "option", "", "特殊选项,逗号分隔")
	flag.StringVar(&split_arrs, "arr", "", "字符串分割数组")
	flag.StringVar(&split_flag, "arrSeparator", "|", "分割符")
	flag.Parse()

	for _, v := range strings.Split(i64, ",") {
		if v != "" {
			opt.Int64s[v] = true
		}
	}
	for _, v := range strings.Split(u64, ",") {
		if v != "" {
			opt.Uint64s[v] = true
		}
	}
	for _, v := range strings.Split(str, ",") {
		if v != "" {
			opt.Strings[v] = true
		}
	}
	for _, v := range strings.Split(f64, ",") {
		if v != "" {
			opt.Float64s[v] = true
		}
	}
	for _, v := range strings.Split(f32, ",") {
		if v != "" {
			opt.Float32s[v] = true
		}
	}
	for _, v := range strings.Split(arr, ",") {
		if v != "" {
			opt.ArrPrefix[v] = v + "s"
		}
	}
	for _, v := range strings.Split(discard, ",") {
		if v != "" {
			opt.Discard[v] = true
		}
	}
	for _, v := range strings.Split(option, ",") {
		if v != "" {
			opt.Options[v] = true
		}
	}
	for _, v := range strings.Split(split_arrs, ",") {
		if v != "" {
			opt.SplitArrs[v] = toCamelCase(v) + "s"
		}
	}
	return opt
}

// 解析xml，获取所有字段
func parseXMLFields(opt *Options) *TemplateData {
	tpl := &TemplateData{}

	xmlFile := opt.InFile
	data, err := ioutil.ReadFile(xmlFile)
	if err != nil {
		fmt.Printf("读取XML文件失败: %v\n", err)
		os.Exit(1)
	}
	xmlStr := string(data)
	lines := strings.Split(xmlStr, "\n")
	if len(lines) < 6 {
		fmt.Println("XML文件行数不足")
		os.Exit(1)
	}
	note := strings.TrimPrefix(lines[1], "<!--")
	note = strings.TrimSuffix(note, "-->")
	note_arr := strings.Split(note, " ")
	note_map := make(map[string]string, len(note_arr))
	for _, one := range note_arr {
		note_values := strings.Split(one, "=")
		if len(note_values) == 2 {
			note_map[note_values[0]] = note_values[1]
		}
	}
	tpl.noteMap = note_map

	var root XMLRoot
	if err := xml.Unmarshal(data, &root); err != nil {
		fmt.Printf("解析XML失败: %v\n", err)
		os.Exit(1)
	}
	if len(root.Datas) == 0 {
		fmt.Println("XML中没有找到数据")
		os.Exit(1)
	}

	// 提取文件名和结构体名
	baseName := filepath.Base(opt.InFile)
	ext := filepath.Ext(baseName)
	tpl.StructName = toCamelCase(strings.TrimSuffix(baseName, ext)) //转成结构体名
	tpl.shortName = baseName[:1]
	tpl.this = baseName[:1] + "."
	tpl.fileName = baseName

	var fields []FieldInfo
	var exist_key bool
	for _, attr := range root.Datas[0].Attrs {
		fieldName := attr.Name.Local
		field_discard := false
		for dis_field := range opt.Discard {
			disField := strings.TrimSuffix(dis_field, "*")
			if strings.Contains(fieldName, disField) {
				field_discard = true
				break
			}
		}
		if field_discard {
			continue
		}
		//if opt.Discard[fieldName] {
		//	continue
		//}
		fType := fieldType(fieldName, opt, note_map)
		fields = append(fields, FieldInfo{
			Name:   toCamelCase(fieldName),
			Type:   fType,
			XMLTag: fieldName,
		})
		if fieldName == opt.Key {
			exist_key = true
		}
	}
	if !exist_key {
		opt.Key = fields[0].Name
		//fmt.Println("XML中没有找到主键")
		//os.Exit(1)
	}

	var resourceArrays []ResourceArray
	var attributeArrays []AttributeArray
	var sliceArrays []SliceArray

	// 分组处理字段(资源组和属性组)
	var resourceGroups = make(map[string][][3]string)
	var attributeGroups = make(map[string][][2]string)
	var attrPrefix = make(map[string]string)
	var sliceGroups = make(map[string][]string)
	for i := 0; i < len(fields); i++ {
		field := fields[i]
		fieldName := field.XMLTag

		//检查数组前缀 skill1、skill2 或 monster_id1、monster_id2
		for arr_step, _ := range opt.ArrPrefix {
			if strings.Contains(fieldName, arr_step) {
				index := strings.Index(fieldName, arr_step)
				prefix, suffix := fieldName[:index], fieldName[index+len(arr_step):]
				if len(prefix) <= 0 && len(suffix) <= 3 {
					tmp := toCamelCase(arr_step) + "s"
					sliceGroups[tmp] = append(sliceGroups[tmp], fieldName)
					break
				}
			}
		}
		//字符串切割成数组
		if val, exist := opt.SplitArrs[fieldName]; exist {
			tpl.SplitArrays = append(tpl.SplitArrays, SplitData{
				fieldName:  fieldName,
				structName: val,
			})
		}
	}

	for i := 0; i < len(fields); {
		field := fields[i]
		fieldName := field.XMLTag
		if !opt.Options["disable_resource"] {
			// 检查是否是资源组(type,value,size)
			res_step := "type"
			if strings.Contains(fieldName, res_step) {
				if i+2 < len(fields) {
					index := strings.Index(fieldName, res_step)
					prefix, suffix := fieldName[:index], fieldName[index+len(res_step):]
					valueField := prefix + "value" + suffix
					sizeField := prefix + "size" + suffix

					if fields[i+1].XMLTag == valueField && fields[i+2].XMLTag == sizeField {
						//是资源数组
						tmp := toCamelCase(prefix) + "ClRes"
						resourceGroups[tmp] = append(resourceGroups[tmp], [3]string{fieldName, valueField, sizeField})
						i += 3
						continue
					}
				}
			}
		}
		if opt.Options["enable_attr"] {
			// 检查是否是属性组(attr_type,attr_value)
			attr_step := "attr_type"
			if strings.Contains(fieldName, attr_step) {
				if i+1 < len(fields) {
					index := strings.Index(fieldName, attr_step)
					prefix, suffix := fieldName[:index], fieldName[index+len(attr_step):]
					valueField := prefix + "attr_value" + suffix
					if fields[i+1].XMLTag == valueField {
						//是属性数组
						tmp := toCamelCase(prefix) + "Attributes"
						attributeGroups[tmp] = append(attributeGroups[tmp], [2]string{fieldName, valueField})
						attrPrefix[tmp] = prefix
						i += 2
						continue
					}
				}
			}
		}
		i++
	}

	// 处理资源组
	for tmpName, typePairs := range resourceGroups {
		resourceArrays = append(resourceArrays, ResourceArray{
			Name:      tmpName,
			TypePairs: typePairs,
		})
	}

	// 处理属性组
	for tmpName, typePair := range attributeGroups {
		prefix := toCamelCase(attrPrefix[tmpName])
		attributeArrays = append(attributeArrays, AttributeArray{
			Name:      tmpName,
			TypePairs: typePair,
			calcFunc:  "Calc" + prefix + "BattleAttrs",
			addFunc:   "Add" + prefix + "Attrs",
		})
	}
	// 处理数组
	for tmpName, typePairs := range sliceGroups {
		sliceArrays = append(sliceArrays, SliceArray{
			Name:      tmpName,
			TypePairs: typePairs,
		})
	}
	//fmt.Println(opt.ArrPrefix, sliceArrays)
	tpl.Key = toCamelCase(opt.Key)
	tpl.Fields = fields
	tpl.ResourceArrays = resourceArrays
	tpl.AttributeArrays = attributeArrays
	tpl.SliceArrays = sliceArrays
	return tpl
}

// 驼峰命名
// 辅助函数
func toCamelCase(s string) string {
	var result strings.Builder
	parts := strings.Split(s, "_")
	for _, part := range parts {
		if part == "" {
			continue
		}
		result.WriteString(strings.Title(part))
	}
	return result.String()
}

// 字段类型推断
func fieldType(field string, opt *Options, note_map map[string]string) string {
	if opt.Discard[field] {
		return ""
	}
	if opt.Int64s[field] {
		return "int64"
	}
	if opt.Uint64s[field] {
		return "uint64"
	}
	if opt.Strings[field] {
		return "string"
	}
	if opt.Float64s[field] {
		return "float64"
	}
	if opt.Float32s[field] {
		return "float32"
	}
	//if note, exist := note_map[field]; exist {
	//	note_arr := strings.Split(note, ":")
	//	not_type := note_arr[0]
	//	if not_type == "int" {
	//		return "uint32"
	//	}
	//	return not_type
	//}
	return "uint32"
}
func getKeyType(tpl *TemplateData) string {
	for _, k := range tpl.Fields {
		if k.Name == tpl.Key {
			return k.Type
		}
	}
	return "uint32"
}
func fieldNote(field string, tpl *TemplateData, is_src bool) string {
	note, exist := tpl.noteMap[field]
	if exist {
		return "//" + note
	}
	if is_src {
		return "//"
	}
	return "//非xml原始字段"
}

func IsResourceOrAttr(fieldName string, tpl *TemplateData) bool {
	for _, k := range tpl.ResourceArrays {
		for _, one := range k.TypePairs {
			for i := 0; i < len(one); i++ {
				if fieldName == one[i] {
					return true
				}
			}
		}
	}
	for _, k := range tpl.AttributeArrays {
		for _, one := range k.TypePairs {
			for i := 0; i < len(one); i++ {
				if fieldName == one[i] {
					return true
				}
			}
		}
	}
	for _, k := range tpl.SliceArrays {
		for _, one := range k.TypePairs {
			if fieldName == one {
				return true
			}
		}
	}
	return false
}
func genResourceAndAttrFields(tpl *TemplateData) ([]string, []string) {
	var structFields []string
	var infoBodys []string
	for _, k := range tpl.ResourceArrays {
		var noteInfo string
		for _, one := range k.TypePairs {
			noteInfo += "\t//" + one[0] + " " + one[1] + " " + one[2] + "\n"
		}
		structFields = append(structFields, strings.TrimRight(noteInfo, "\n"))
		structFields = append(structFields, fmt.Sprintf("\t%s %s `xml:\"%s\"` %s", k.Name, "[]*cl.Resource", "-", fieldNote(k.Name, tpl, false)))

		tmpInfo := "\t//资源" + k.Name
		infoBodys = append(infoBodys, tmpInfo)
		num := len(k.TypePairs)
		tmpInfo = "\t" + tpl.this + k.Name + " = make([]*cl.Resource, 0, " + strconv.Itoa(num) + "/2)"
		infoBodys = append(infoBodys, tmpInfo)
		for _, one := range k.TypePairs {
			resType := toCamelCase(one[0])
			resValue := toCamelCase(one[1])
			resSize := toCamelCase(one[2])
			tmpInfo = "\tif " + tpl.this + resType + " > 0 && " + tpl.this + resSize + " > 0 {\n"
			tmpInfo += "\t\t" + tpl.this + k.Name + " = append(" + tpl.this + k.Name + ", \n"
			tmpInfo += "\t\t\t&cl.Resource{Type: " + tpl.this + resType + ", Value: " + tpl.this + resValue + ", Count: " + tpl.this + resSize + "})\n"
			tmpInfo += "\t}"
			infoBodys = append(infoBodys, tmpInfo)
		}
		tmpInfo = "\t" + tpl.this + k.Name + " = " + tpl.this + k.Name + "[:len(" + tpl.this + k.Name + "):len(" + tpl.this + k.Name + ")]"
		infoBodys = append(infoBodys, tmpInfo)
	}
	for _, k := range tpl.SliceArrays {
		var noteInfo = "\t//合并"
		for _, one := range k.TypePairs {
			noteInfo += one + ","
		}
		structFields = append(structFields, strings.TrimRight(noteInfo, ","))
		structFields = append(structFields, fmt.Sprintf("\t%s %s `xml:\"%s\"` %s", k.Name, "[]uint32", "-", fieldNote(k.Name, tpl, false)))

		one := k.TypePairs
		tmpInfo := "\t//合并前缀为" + strings.ToLower(k.Name[:len(k.Name)-1]) + "的数组"
		infoBodys = append(infoBodys, tmpInfo)
		tmpInfo = "\tall" + k.Name + " := []uint32{"
		for i := 0; i < len(one); i++ {
			tmpInfo += tpl.this + toCamelCase(one[i])
			if i != len(one)-1 {
				tmpInfo += ","
			} else {
				tmpInfo += "}"
			}
		}
		infoBodys = append(infoBodys, tmpInfo)
		num := len(one)
		tmpInfo = "\t" + tpl.this + k.Name + " = make([]uint32, 0, " + strconv.Itoa(num) + "/2)"
		infoBodys = append(infoBodys, tmpInfo)
		tmpInfo = "\tfor _, value := range all" + k.Name + "{\n"
		tmpInfo += "\t\tif value != 0 {\n"
		tmpInfo += "\t\t\t" + tpl.this + k.Name + " = append(" + tpl.this + k.Name + ", value)\n"
		tmpInfo += "\t\t}\n"
		tmpInfo += "\t}"
		infoBodys = append(infoBodys, tmpInfo)
		tmpInfo = "\t" + tpl.this + k.Name + " = " + tpl.this + k.Name + "[:len(" + tpl.this + k.Name + "):len(" + tpl.this + k.Name + ")]\n"
		infoBodys = append(infoBodys, tmpInfo)
	}
	for _, k := range tpl.SplitArrays {
		fieldname, structName := k.fieldName, k.structName
		camelFieldName := toCamelCase(fieldname)
		structFields = append(structFields, "\t//拆分"+fieldname+",分隔符'|',之后将"+fieldname+"置空")
		structFields = append(structFields, fmt.Sprintf("\t%s %s `xml:\"%s\"` %s", structName, "[]uint32", "-", fieldNote("", tpl, false)))

		tmpInfo := "\t//拆分字段" + camelFieldName + "为数组\n"
		tmpInfo += "\t" + tpl.this + structName + " = nil\n"
		tmpInfo += "\tif " + tpl.this + camelFieldName + " != \"\" {\n"
		tmpInfo += "\t\tif vals, err := util.StringToUint32SliceErr(" + tpl.this + camelFieldName + ", \"|\"); err != nil {\n"
		tmpInfo += "\t\t\tpanic(fmt.Sprintf(\"split " + tpl.fileName + " " + camelFieldName + " error:%s\", err))\n"
		tmpInfo += "\t\t } else {\n"
		tmpInfo += "\t\t\t" + tpl.this + structName + " = vals\n"
		tmpInfo += "\t\t\t" + tpl.this + camelFieldName + " = \"\" //释放内存\n"
		tmpInfo += "\t\t}\n"
		tmpInfo += "\t}\n"
		infoBodys = append(infoBodys, tmpInfo)
	}
	for _, k := range tpl.AttributeArrays {
		var noteInfo string
		for _, one := range k.TypePairs {
			noteInfo += "\t// " + one[0] + ", " + one[1] + "\n"
		}
		structFields = append(structFields, strings.TrimRight(noteInfo, "\n"))
		structFields = append(structFields, fmt.Sprintf("\t%s %s `xml:\"%s\"` %s", k.Name, "[]goxml.AttrKV", "-", fieldNote(k.Name, tpl, false)))

		tmpInfo := "\t//合并前缀为" + strings.ToLower(k.Name[:len(k.Name)-1]) + "的游戏属性"
		infoBodys = append(infoBodys, tmpInfo)
		num := len(k.TypePairs)
		tmpInfo = "\t" + tpl.this + k.Name + " = make([]goxml.AttrKV, 0, " + strconv.Itoa(num) + "/2)"
		infoBodys = append(infoBodys, tmpInfo)
		for _, one := range k.TypePairs {
			attrType := toCamelCase(one[0])
			attrValue := toCamelCase(one[1])
			tmpInfo = "\tif " + tpl.this + attrType + " != 0 && " + tpl.this + attrType + " < uint32(common.ATTRINDEX_MAX) && " + tpl.this + attrValue + " != 0 {\n"
			tmpInfo += "\t\t" + tpl.this + k.Name + " = append(" + tpl.this + k.Name + ", goxml.AttrKV{K: " + tpl.this + attrType + ", V: uint32(" + tpl.this + attrValue + ")})\n"
			tmpInfo += "\t}"
			infoBodys = append(infoBodys, tmpInfo)
		}
		tmpInfo = "\t" + tpl.this + k.Name + " = " + tpl.this + k.Name + "[:len(" + tpl.this + k.Name + "):len(" + tpl.this + k.Name + ")]"
		infoBodys = append(infoBodys, tmpInfo)
	}
	return structFields, infoBodys
}

// 生成Go结构体代码
func genGoCode(opt *Options, tpl *TemplateData) string {
	structName := tpl.StructName
	tempStructName := structName + "Temp"
	managerName := structName + "Manager"
	infosName := structName + "s"
	keyType := getKeyType(tpl)
	beginNote, endNote := "", ""
	if !opt.Simple {
		beginNote, endNote = "/*", "*/"
	}

	// 结构体字段
	var structFields, infoBodys, tempFields []string
	var userDataBodys []string
	structFields, infoBodys = genResourceAndAttrFields(tpl)
	if opt.Options["enable_userdata"] {
		structFields = append(structFields, "\t//自定义数据")
		structFields = append(structFields, fmt.Sprintf("\t%s %s `xml:\"%s\"` %s", "userData", "interface{}", "-", fieldNote("", tpl, false)))
		userDataBodys = append(userDataBodys, "type "+tpl.StructName+"UserData struct{")
		userDataBodys = append(userDataBodys, "\t//TODO 添加自定义字段")
		userDataBodys = append(userDataBodys, "}\n")
		userDataBodys = append(userDataBodys, "func ("+tpl.shortName+" *"+tpl.StructName+") prepareUserData(){")
		userDataBodys = append(userDataBodys, "\tud := &"+tpl.StructName+"UserData{}")
		userDataBodys = append(userDataBodys, "\t//TODO 加载自定义字段")
		userDataBodys = append(userDataBodys, "\t"+tpl.shortName+".userData = ud")
		userDataBodys = append(userDataBodys, "}")
		userDataBodys = append(userDataBodys, "func ("+tpl.shortName+" *"+tpl.StructName+") getUserData() *"+tpl.StructName+"UserData {")
		userDataBodys = append(userDataBodys, "\treturn "+tpl.shortName+".userData.(*"+tpl.StructName+"UserData)")
		userDataBodys = append(userDataBodys, "}")
	}
	for _, k := range tpl.Fields {
		if opt.Discard[k.XMLTag] {
			continue
		}
		ft := k.Type
		if ft == "" {
			continue
		}
		field := k.Name
		if !opt.Options["keep_resource"] && IsResourceOrAttr(k.XMLTag, tpl) {
			tempFields = append(tempFields, fmt.Sprintf("\t%s %s `xml:\"%s,attr\"` %s", field, ft, k.XMLTag, fieldNote(k.XMLTag, tpl, true)))
			continue
		}
		structFields = append(structFields, fmt.Sprintf("\t%s %s `xml:\"%s,attr\"` %s", field, ft, k.XMLTag, fieldNote(k.XMLTag, tpl, true)))
	}

	var prepareBodys []string
	if !opt.Options["enable_userdata"] {
		prepareBodys = append(prepareBodys, "func ("+tpl.shortName+"*"+structName+") prepare() {\n}")
	} else {
		prepareBodys = append(prepareBodys, "func ("+tpl.shortName+"*"+structName+") prepare() {")
		prepareBodys = append(prepareBodys, "\t//自定义数据")
		prepareBodys = append(prepareBodys, "\t"+tpl.this+"prepareUserData()")
		prepareBodys = append(prepareBodys, "}")
	}

	var attrBodys []string
	for _, k := range tpl.AttributeArrays {
		attrBodys = append(attrBodys, "func ("+tpl.shortName+" *"+structName+")"+k.calcFunc+"(attrs []int64) {")
		attrBodys = append(attrBodys, "\tfor _, v := range "+tpl.this+k.Name+" {")
		attrBodys = append(attrBodys, "\t\tattrs[v.K] += int64(v.V)")
		attrBodys = append(attrBodys, "\t}")
		attrBodys = append(attrBodys, "}")
		attrBodys = append(attrBodys, "func ("+tpl.shortName+" *"+structName+")"+k.addFunc+"(attrs map[uint32]int64) {")
		attrBodys = append(attrBodys, "\tfor _, v := range "+tpl.this+k.Name+" {")
		attrBodys = append(attrBodys, "\t\tattrs[v.K] += int64(v.V)")
		attrBodys = append(attrBodys, "\t}")
		attrBodys = append(attrBodys, "}")
	}

	// 生成代码
	code := `package goxml1

import (
	"io"
	"fmt"
	"strconv"
	"path/filepath"
	"encoding/xml"

	"app/goxml"
	"app/protos/out/cl"
	"app/protos/out/common"

	"gitee.com/lovengame/kit/sea/util"
	l4g "github.com/ivanabc/log4go"
)

// Reference imports to suppress errors if they are not otherwise used.
var (
	_ = fmt.Println
	_ = filepath.Separator
	_ = cl.SmallKV{}
	_ = common.FUNCID_MODULE_NONE
	_ = util.None{}
	_ = l4g.Info
	_ = strconv.IntSize
)

type ` + structName + ` struct {
` + strings.Join(structFields, "\n") + `
}

type ` + tempStructName + ` struct {
	*` + structName + `
` + strings.Join(tempFields, "\n") + `
}

func (` + tpl.shortName + ` *` + tempStructName + `) Info() *` + structName + ` {
` + strings.Join(infoBodys, "\n") + `
	return ` + tpl.this + structName + `
}

` + strings.Join(prepareBodys, "\n") + `

` + strings.Join(attrBodys, "\n") + `

type ` + infosName + ` struct {
	Datas []*` + structName + ` ` + "`xml:\"data\"`" + `
}

// 实现大文件并发加载接口
func (` + tpl.shortName + ` *` + infosName + `) Spawn() util.XMLSplitTargeter {
	return &` + infosName + `{}
}

func (` + tpl.shortName + ` *` + infosName + `) Merge(tar util.XMLSplitTargeter) {
	` + tpl.this + `Datas = append(` + tpl.this + `Datas, tar.(*` + infosName + `).Datas...)
}

// unmarshal加速
func (` + tpl.shortName + ` *` + tempStructName + `) UnmarshalXMLAttr(attr xml.Attr) error {
	switch attr.Name.Local {
`
	for _, k := range tpl.Fields {
		ft := k.Type
		if ft == "" {
			continue
		}
		field := k.Name
		switch ft {
		case "int64":
			code += fmt.Sprintf(`	case "%s":
		pv, err := strconv.ParseInt(attr.Value, 10, 64)
		if err != nil {
			return err
		}
		`+tpl.shortName+`.%s = pv
`, k.XMLTag, field)
		case "uint64":
			code += fmt.Sprintf(`	case "%s":
		pv, err := strconv.ParseUint(attr.Value, 10, 64)
		if err != nil {
			return err
		}
		`+tpl.shortName+`.%s = pv
`, k.XMLTag, field)
		case "string":
			code += fmt.Sprintf(`	case "%s":
		`+tpl.shortName+`.%s = attr.Value
`, k.XMLTag, field)
		case "float64":
			code += fmt.Sprintf(`	case "%s":
		pv, err := strconv.ParseFloat(attr.Value, 64)
		if err != nil {
			return err
		}
		`+tpl.shortName+`.%s = pv
`, k.XMLTag, field)
		case "float32":
			code += fmt.Sprintf(`	case "%s":
		pv, err := strconv.ParseFloat(attr.Value, 32)
		if err != nil {
			return err
		}
		`+tpl.shortName+`.%s = float32(pv)
`, k.XMLTag, field)
		default: // uint32
			code += fmt.Sprintf(`	case "%s":
		pv, err := strconv.ParseUint(attr.Value, 10, 32)
		if err != nil {
			return err
		}
		`+tpl.shortName+`.%s = uint32(pv)
`, k.XMLTag, field)
		}
	}
	code += `	default:
		//l4g.Errorf("` + tpl.fileName + ` UnmarshalXMLAttr %s unsupport", attr.Name.Local)
	}
	return nil
}

func (` + tpl.shortName + ` *` + infosName + `) UnmarshalXML(decode *xml.Decoder, start xml.StartElement) error {
	for {
		token, err := decode.Token()
		if err != nil {
			if err == io.EOF {
				return nil
			}
			return err
		}
		temp := ` + tempStructName + `{` + structName + `: &` + structName + `{}}`
	code += `
		switch to := token.(type) {
		case xml.StartElement:
			//NOTE: 策划导出的xml不包含嵌套element
			for _, attr := range to.Attr {
				if attr.Value == "" {
					continue
				}
				if err := temp.UnmarshalXMLAttr(attr); err != nil {
					return fmt.Errorf("` + tpl.fileName + ` UnmarshalXMLAttr:name=%s value=%s err:%s", attr.Name.Local, attr.Value, err)
				}
			}
			` + tpl.this + `Datas = append(` + tpl.this + `Datas, temp.Info())
		case xml.EndElement:
			if to == start.End() {
				break
			}
		}
	}
	return nil
}
` + beginNote + `
` + strings.Join(userDataBodys, "\n") + `
func (` + tpl.shortName + ` *` + structName + `) Check() error{
	return nil
}

type ` + managerName + ` struct {
	Datas map[` + keyType + `]*` + structName + `
}

func (` + tpl.shortName + ` *` + managerName + `) Load(dir string, show bool) {
	tmp := &` + infosName + `{}
	fileName := filepath.Join(dir, "` + filepath.Base(opt.InFile) + `")
	if err := util.LoadConfig(fileName, tmp); err != nil {
		panic(fmt.Sprintf("load config ` + tpl.fileName + ` fail: %s", err))
	} else {
		for _, data := range tmp.Datas {
			data.prepare()
			if err := data.Check(); err != nil {
				l4g.Debugf("config(` + tpl.fileName + `): %+v check error:%s", data, err)
			}
			if show {
				l4g.Debugf("config(` + tpl.fileName + `): %+v", data)
			}
		}
	}
	if ` + tpl.this + `Datas == nil {
		` + tpl.this + `Datas = make(map[` + keyType + `]*` + structName + `, len(tmp.Datas))
	}
	for _, data := range tmp.Datas {
		if ptr, exist := ` + tpl.this + `Datas[data.` + tpl.Key + `]; exist {
			*ptr = *data
			data = ptr
		} else {
			` + tpl.this + `Datas[data.` + tpl.Key + `] = data
		}
	}
}

func (` + tpl.shortName + ` *` + managerName + `) Index(key ` + keyType + `) *` + structName + ` { return ` + tpl.this + `Datas[key] }
func (` + tpl.shortName + ` *` + managerName + `) FileName() string { return "` + filepath.Base(opt.InFile) + `" }
` + endNote + `
`
	return code
}

func main() {
	opt := parseFlags()
	if opt.InFile == "" {
		fmt.Println("请指定输入xml文件 -in")
		return
	}
	tpl := parseXMLFields(opt)
	code := genGoCode(opt, tpl)
	outFile := filepath.Join(opt.OutPath, strings.TrimSuffix(filepath.Base(opt.InFile), filepath.Ext(opt.InFile))+".go")
	if err := ioutil.WriteFile(outFile, []byte(code), 0644); err != nil {
		fmt.Println("写入go文件失败:", err)
		return
	}
	//fmt.Println("生成成功:", outFile)
}
